*******************************************************************************
* List Manager/LineEdit Demo
*
* (C)  Copyright Apple Computer, Inc. 1988
* All rights reserved.
*
* by Keith Rollin and Jim Mensch
* July 8, 1988
*
* This program demonstrates the use of lists and LineEdit items in a window. It
* uses controls to create, rename, remove and sort items in a list, and uses a
* LineEdit item to enter new and rename old items. Cut, Copy, Paste, and Clear
* are supported.
*
* Demo Index:
*               Creating and changing lists
*               Simple button controls in a window
*               LineEdit Event handling
*               Cut, Copy, Paste, and Clear
*
*
* Files: System macros and equates.
*        Lists.Inits.aii
*
*
* Modification History:
*
* Version 1.0   Mensch & Rollin
*
*      July 7, 1988
*
*      First Release
*
*******************************************************************************
*                                                                    *
*             Apple IIGS Source Code Sampler, Volume I               *
*                                                                    *
*             Copyright (c) Apple Computer, Inc. 1988                *
*                       All Rights Reserved                          *
*                                                                    *
*            Written by Apple II Developer Tech Support              *
*                                                                    *
*                                                                    *
*                                                                    *
*  ----------------------------------------------------------------  *
*                                                                    *
*     This program and its derivatives are licensed only for         *
*     use on Apple computers.                                        *
*                                                                    *
*     Works based on this program must contain and                   *
*     conspicuously display this notice.                             *
*                                                                    *
*     This software is provided for your evaluation and to           *
*     assist you in developing software for the Apple IIGS           *
*     computer.                                                      *
*                                                                    *
*     DISCLAIMER OF WARRANTY                                         *
*                                                                    *
*     THE SOFTWARE IS PROVIDED "AS IS" WITHOUT                       *
*     WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,               *
*     WITH RESPECT TO ITS MERCHANTABILITY OR ITS FITNESS             *
*     FOR ANY PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO             *
*     THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH            *
*     YOU.  SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU (AND            *
*     NOT APPLE OR AN APPLE AUTHORIZED REPRESENTATIVE)               *
*     ASSUME THE ENTIRE COST OF ALL NECESSARY SERVICING,             *
*     REPAIR OR CORRECTION.                                          *
*                                                                    *
*     Apple does not warrant that the functions                      *
*     contained in the Software will meet your requirements          *
*     or that the operation of the Software will be                  *
*     uninterrupted or error free or that defects in the             *
*     Software will be corrected.                                    *
*                                                                    *
*     SOME STATES DO NOT ALLOW THE EXCLUSION                         *
*     OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY              *
*     NOT APPLY TO YOU.  THIS WARRANTY GIVES YOU SPECIFIC            *
*     LEGAL RIGHTS AND YOU MAY ALSO HAVE OTHER RIGHTS                *
*     WHICH VARY FROM STATE TO STATE.                                *
*                                                                    *
*                                                                    *
**********************************************************************



                mcopy lists.macros
                copy 2/ainclude/E16.Quickdraw
                copy 2/ainclude/E16.Memory
                copy 2/ainclude/E16.EVENT
                copy 2/ainclude/E16.Control
                copy 2/ainclude/E16.Window
                copy 2/ainclude/E16.Dialog
                copy 2/ainclude/E16.List

*******************************************************************************
*
*    Equates used in this program.
*
*******************************************************************************
DPHandle        gequ 0          ; Handle to Tool Direct Page area
DPPointer       gequ DPHandle+4 ; Pointer to Tool Direct Page area
deref           gequ DPPointer+4                ; Temporary Handle dereference area
TempLong        gequ deref+4
TempWord        gequ TempLong+4

ScreenMode      gequ $80
ScreenWidth     gequ 640


                EJECT
*******************************************************************************
*
Main            start
*
* Description:  This is the main routine. It calls routines to Initialize
*               the tools, initialize application specific data, run the
*               main EventLoop, close the application, and close the tools.
*               Then it calls the ProDOS Quit command.
*
*
* Inputs:       NONE
*
* Outputs:      NONE
*
* External Refs:
*               Import InitTools
*               Import InitApp
*               Import EventLoop
*               Import CloseApp
*               Import CloseTools
*               Import QuitParms
*
* Entry Points: NONE
*
*******************************************************************************
                Using Globals

                jsr InitTools
                jsr InitApp

                _ShowCursor

                jsr EventLoop

                jsr CloseApp
                jsr CloseTools

                _Quit QuitParms

                end

                EJECT
*******************************************************************************
*
Globals         data
*
* Description:  Holder of all of our data.
*
*
* Inputs:       N/A
*
* Outputs:      N/A
*
* External Refs:
*               Import PrUpdate
*               Import PrOldUpdate
*
* Entry Points:
*               Export QuitParms                ; used by Main
*
*******************************************************************************
*
* Standard global data
*
*******************************************************************************

TitleString     str 'Apple IIgs List Mgr/LineEdit Example Application'
AutString       str 'By Rollin and Mensch Apple DTS )1988 Apple Computer'
VersString      str 'Version: 1.0'

MenuHeight      ds 2            ; Stored height of menu bar
MyID            ds 2            ; Application ID
MyDP            ds 2            ; My direct page storage

QuitFlag        ds 2
QuitParms       dc i4'0'        ; Pathname of next app
                dc i2'$00'      ; flags

EventRecord     ANOP
EventWhat       ds 2
EventMessage    ds 4
EventWhen       ds 4
EventWhere      ds 4
EventModifiers  ds 2
TaskData        ds 4
TaskMask        dc i4'$0000FFFF'

                EJECT
*******************************************************************************
*
* Application specific global data
*
*******************************************************************************

; This is a list of pointers to the text that is used to create our menus. It
; is used by InitApp to find all of the menu templates and use them to create
; our menubar. This loop loads MenuPtrLen-4 into an index, gets the
; corresponding menu template pointer in this table, and uses that in a
; NewMenu call. It then decrements the index by 4, and repeats the procees
; until the index is negative.

MenuPtr         dc i4'AppMenu'
                dc i4'FileMenu'
                dc i4'EditMenu'
MenuPtrLen      equ *-MenuPtr


; Menu list: menu items should be numbered consecutivly starting from 250.
; As a convention, use 256 as about and 257 as Quit.

AppMenu         dc c'$$@\N1',h'0D'
                dc c'--About List Manager/LineEdit Demo...\N256V',h'00'
                dc c'.'
FileMenu        dc c'$$  File  \N2',h'00'
                dc c'--Close Document\N255DV',h'00'
                dc c'--Quit\N257*Qq',h'00'
                dc c'.'
EditMenu        dc c'$$  Edit  \N3',h'00'
                dc c'--Undo\N250*ZzVD',h'00'
                dc c'--Cut\N251*Xx',h'00'
                dc c'--Copy\N252*Cc',h'00'
                dc c'--Paste\N253*Vv',h'00'
                dc c'--Clear\N254',h'00'
                dc c'.'

ErrOut          ds 4
TempSave        ds 4
tempHndl        ds 4

;
;   Control definitions for use with new control
;
NumCtrls        dc i2'5'
MyCtrlList      dc i4'NewBRec,DelBRec,RepBRec,RstBRec,SortBRec'

MyCtlHdls       ANOP
NewBHdl         ds 4            ; storage for returned handles
DelBHdl         ds 4
RepBHdl         ds 4
RstBHdl         ds 4
SortBHdl        ds 4
LEHandle        ds 4

NewBTitle       str 'New Entry'
DelBTitle       str 'Remove'
RepBTitle       str 'Replace'
RstBTitle       str 'UnHilite'
SortBTitle      str 'Sort'

NewBRect        dc i2'10,250,25,350'
DelBRect        dc i2'30,250,45,350'
RepBRect        dc i2'50,250,65,350'
RstBRect        dc i2'70,250,85,350'
SortBRect       dc i2'90,250,105,350'
destRect        dc i2'100,20,110,224'
viewRect        dc i2'97,10,113,234'

NewBRec         ANOP
                dc i4'0'
                dc i4'1'
                dc i4'SimpleProc'               ; defproc = simple button
                dc i2'0,0'      ; no params
                dc i2'1'        ; starting value
                dc i2'BoldButton'               ; control flags
                dc i4'NewBTitle'                ; pointer to title string
                dc i4'NewBRect' ; pointer to bounds rect
cRecSize        equ *-NewBRec   ; size of my control record

DelBRec         ANOP
                dc i4'0'        ; color table pointer
                dc i4'2'        ; refcon ( control dispatch number )
                dc i4'SimpleProc'               ; defproc = simple button
                dc i2'0,0'      ; no params
                dc i2'1'        ; starting value
                dc i2'SimpRound'                ; control flags
                dc i4'DelBTitle'                ; pointer to title string
                dc i4'DelBRect' ; pointer to bounds rect


RepBRec         ANOP
                dc i4'0'        ; color table pointer
                dc i4'3'        ; refcon ( control dispatch number )
                dc i4'SimpleProc'               ; defproc = simple button
                dc i2'0,0'      ; no params
                dc i2'1'        ; starting value
                dc i2'SimpRound'                ; control flags
                dc i4'RepBTitle'                ; pointer to title string
                dc i4'RepBRect' ; pointer to bounds rect

RstBRec         ANOP
                dc i4'0'        ; color table pointer
                dc i4'4'        ; refcon ( control dispatch number )
                dc i4'SimpleProc'               ; defproc = simple button
                dc i2'0,0'      ; no params
                dc i2'1'        ; starting value
                dc i2'SimpRound'                ; control flags
                dc i4'RstBTitle'                ; pointer to title string
                dc i4'RstBRect' ; pointer to bounds rect

SortBRec        ANOP
                dc i4'0'        ; color table pointer
                dc i4'6'        ; refcon ( control dispatch number )
                dc i4'SimpleProc'               ; defproc = simple button
                dc i2'0,0'      ; no params
                dc i2'1'        ; starting value
                dc i2'SimpRound'                ; control flags
                dc i4'SortBTitle'               ; pointer to title string
                dc i4'SortBRect'                ; pointer to bounds rect


FullRect        dc i2'0,0,120,400'
ListCtlHndl     ds 4

MemberSize      equ 10

MyList          ANOP            ; List record
ListRect        dc i2'10,10,92,210'             ; list is 200x80
MLNum           dc i2'5'        ; number of members in the list
                dc i2'8'        ; max seen at once
                dc i2'selectOnlyOne'            ; single selection/pascal strings
                dc i2'1'        ; list start
                dc i4'0'        ; list control handle
                dc i4'0'        ; no list draw
                dc i2'10'       ; list member height
                dc i2'MemberSize'               ; list record size
                dc i4'MyData'   ; pointer to member list
                dc i4'5'        ; list ref con (control dispatch number)
                dc i4'0'        ; color table pointer (default colors)

;
; These are the data records for each member of the list. Each one consists of:
;      - a pointer to the string to be printed
;      - a word of bit flags (only the lower byte is used; the upper byte is
;        used as padding to make data access easier.
;      - a 4 byte space to hold a handle for those items whose names have been
;        stored in the heap with the Memory Manager
; We define only 5 members initially. However, we reserve space in the member
; record array for 30 members total.

MyData          dc i4'member1'
                dc i2'0'
                dc i4'0'
                dc i4'member2'
                dc i2'0'
                dc i4'0'
                dc i4'member3'
                dc i2'0'
                dc i4'0'
                dc i4'member4'
                dc i2'0'
                dc i4'0'
                dc i4'member5'
                dc i2'0'
                dc i4'0'
                ds 25*Membersize                ; and at the end, room for 25 more
MyDataTSize     equ *-MyData

member1         str 'Member 1'
member2         str 'Member 2'
member3         str 'Member 3'
member4         str 'Member 4'
member5         str 'Member 5'

MyWindow        dc i2'WindEnd-*'                ; size of paramBlock
                dc i2'%0010000000100000' ; frame bits : alert, vis
                dc i4'0'        ; no title bar, no title...
                dc i4'0'        ; ref con 0
                dc i2'0,0,0,0'  ; zoomed rect
                dc i4'0'        ; color table = default
                dc i2'0,0'      ; scroll bar XY origin
                dc i2'0,0'      ; no scroll no max...
                dc i2'0,0'      ; No zooming...
                dc i2'0,0'      ; no scrolling
                dc i2'0,0'      ; no paging....
                dc i4'0'        ; no info bar, no ref con
                dc i2'0'        ; info bar height (none...)
                dc i4'0'        ; Frame defproc, NIL=standard
                dc i4'0'        ; no info bar def proc
                dc i4'0'        ; no auto updates..(yet)
                dc i2'20,20,140,420'            ; bounds rect...
                dc i4'$FFFFFFFF'                ; plane= on top...
                dc i4'0'        ; NIL storage pointer
WindEnd         equ *
WindPointer     ds 4

                end


                EJECT
*******************************************************************************
*
InitApp         start
*
* Description:  Perform any application specific initialization.
*
* Inputs:       NONE
*
* Outputs:      NONE
*
* External Refs:                
*               Import CreateList
*               Import CreateCtrls
*               Import NoSelection
*
* Entry Points: NONE
*
*******************************************************************************
                using Globals

                stz QuitFlag

                pha
                pha
                PushLong #MyWindow
                _NewWindow
                PullLong WindPointer

                PushLong WindPointer
                _SetPort

                jsr CreateList  ; make a list in the window
                jsr CreateCtrls
                jsr NoSelection ; reset the buttons to unselected

                PushWord #255   ; deactivate the delete button
                PushLong NewBHdl                ; handle to the delete button
                _HiLiteControl

                PushWord #25    ; set the size of LineEdit's
                _LESetScrapLen  ; scrap to 25 characters

                rts
                end

                EJECT
*******************************************************************************
*
CloseApp        start
*
* Description:  Close down things. This disposes of all items and memory
*               that we allocated.
*
*
* Inputs:       NONE
*
* Outputs:      NONE
*
* External Refs:                NONE
*
* Entry Points: NONE
*
*******************************************************************************
                using Globals

                rts
                end

                EJECT
*******************************************************************************
*
EventLoop       start
*
* Description:  Main Event Loop. Handle things until user selects Quit.
*
*
* Inputs:       NONE
*
* Outputs:      NONE
*
* External Refs:
*               Import MenuSelect
*               Import ignore
*               Import doUpdate
*               Import InContent
*               Import doActivate
*               Import doKey
*
* Entry Points: NONE
*
*******************************************************************************
                using Globals

                PushLong LEHandle               ; flash the LineEdit cursor
                _LEIdle

                pha             ; Push on space for TaskMaster result
                PushWord #EveryEvent ; GetNextEvent mask
                PushLong #EventRecord ; Pointer to Event Record
                _TaskMaster

                pla             ; Get TaskMaster result
                beq EventLoop   ; Remove if you want to use null events
                asl A           ; Turn it into an index
                tax
                jsr (TaskTable,x)               ; Call appropriate event handler

                lda QuitFlag    ; Quit selected?
                beq EventLoop   ; no - keep looping

                rts             ; yes- leave the program

TaskTable       dc i2'Ignore'   ; 0 Null
                dc i2'Ignore'   ; 1 MouseDown
                dc i2'Ignore'   ; 2 MouseUp
                dc i2'doKey'    ; 3 KeyDown
                dc i2'Ignore'   ; 4 undefined
                dc i2'doKey'    ; 5 AutoKey
                dc i2'doUpdate' ; 6 Update
                dc i2'Ignore'   ; 7 undefined
                dc i2'doActivate'               ; 8 Activate
                dc i2'Ignore'   ; 9 Switch
                dc i2'Ignore'   ; 10 Desk accessory
                dc i2'Ignore'   ; 11 Device driver
                dc i2'Ignore'   ; 12 ap
                dc i2'Ignore'   ; 13 ap
                dc i2'Ignore'   ; 14 ap
                dc i2'Ignore'   ; 15 ap
                dc i2'Ignore'   ; TASK 0 indesk
                dc i2'MenuSelect'               ; TASK 1 in menuBar
                dc i2'Ignore'   ; TASK 2 in system window
                dc i2'InContent'                ; TASK 3 in content
                dc i2'Ignore'   ; TASK 4 in Drag
                dc i2'Ignore'   ; TASK 5 in grow
                dc i2'Ignore'   ; TASK 6 in goaway
                dc i2'Ignore'   ; TASK 7 in zoom
                dc i2'Ignore'   ; TASK 8 in info bar
                dc i2'MenuSelect'               ; TASK 9 in special             menu
                dc i2'Ignore'   ; TASK 10 in NDA
                dc i2'Ignore'   ; TASK 11 in frame
                dc i2'Ignore'   ; TASK 12 in drop

                end

                EJECT
*******************************************************************************
*
MenuSelect      start
*
* Description:  This routine is called when TaskMaster returns a menu
*               event. It takes the menu item that was hit and calculates
*               an offset into the menu dispatch table. It then calls that
*               routine and unhilites the menu when it is done.
*
* Inputs:       TaskData holds menu item selected.
*
* Outputs:      NONE
*
* External Refs:
*               Import ignore
*               Import doAbout
*               Import doQuit
*               Import doCut
*               Import doCopy
*               Import doPaste
*               Import doClear
*
* Entry Points: NONE
*
*******************************************************************************
                using Globals

                lda TaskData    ; Get the ID of the menu item selected.
                sec             ; Turn it into an index by subtracting
                sbc #250        ; the starting ID number (25) and mul-
                asl a           ; tiplying by 2 (each table entry con-
                tax             ; sists of 2 bytes).
                jsr (menuTable,x)               ; Call the routine behind it.

                PushWord #0     ; Routine done - unhilite the menubar.
                PushWord taskData+2
                _HiLiteMenu

                rts

MenuTable       dc i2'Ignore'   ; undo
                dc i2'doCut'    ; cut
                dc i2'doCopy'   ; copy
                dc i2'doPaste'  ; paste
                dc i2'doClear'  ; clear
                dc i2'Ignore'   ; close
                dc i2'doAbout'
                dc i2'doQuit'
                end

                EJECT
*******************************************************************************
*
Ignore          start
*
* Description:  Called when I want to ignore an event.
*
*
* Inputs:       NONE
*
* Outputs:      NONE
*
* External Refs:                NONE
*
* Entry Points: NONE
*
*******************************************************************************

                rts
                end

                EJECT
*******************************************************************************
*
doUpdate        start
*
* Description:  Called by the Main Event Loop to update a window's
*               contents.
*
*
* Inputs:       NONE
*
* Outputs:      NONE
*
* External Refs:                NONE
*
* Entry Points: NONE
*
*******************************************************************************
                using Globals

                lda WindPointer ; first test to see that its our window
                ora WindPointer+2
                beq NotUp       ; bad bad update event!

                PushLong WindPointer ; Prepare to update our window
                _BeginUpdate

                PushLong WindPointer ; draw all of the controls
                _DrawControls

                PushLong #ViewRect              ; frame our lineEdit item
                _FrameRect

                PushLong LEHandle               ; update the LineEdit item
                _LEUpdate

                PushLong WindPointer ; end updating of our window
                _EndUpdate

NotUp           ANOP
                rts
                end

                EJECT
*******************************************************************************
*
DoActivate      start
*
* Description:  This routine is called when our window is either coming
*               to front, or no longer being in front. Its main function
*               is to make the LineEdit item (in)active.
*
*
* Inputs:       Event record from TaskMaster
*
* Outputs:      NONE
*
* External Refs:                NONE
*
* Entry Points: NONE
*
*******************************************************************************
                using Globals

                PushLong LEHandle
                lda EventModifiers
                and #activeFlag
                bne Activate
                _LEDeactivate
                bra done
Activate        _LEActivate
done            ANOP
                rts
                end

                EJECT
*******************************************************************************
*
doKey           start
*
* Description:  Called by the Main Event loop when a Keydown or AutoKey
*               Event occurs. If the RETURN key was hit, a hit on the
*               "New Item" button is simulated. Otherwise, the key press
*               is passed on to LEKey.
*
*
* Inputs:       Event record from TaskMaster
*
* Outputs:      LineEdit or List is modified
*
* External Refs:                
*               Import NewListItem
*               Import ckNewAndRep
*
* Entry Points: NONE
*
*******************************************************************************
                using Globals

                lda EventMessage                ; get the character pressed
                and #$007f      ; clear the high if it's set
                cmp #$0D        ; see if it's a <RETURN> 
                beq doCR        ; if is one, simulate New Item hit
                PushWord EventMessage ; else, pass this on to LEKey
                PushWord EventModifiers
                PushLong LEHandle
                _LEKey

                jsr ckNewAndRep ; update the New and Replace buttons
                bra done

doCR            ANOP
                PushWord #1     ; Hilite the New button
                PushLong NewBHdl
                _HiLiteControl

                PushWord #0     ; UnHilite the New button
                PushLong NewBHdl
                _HiLiteControl

                jsr NewListItem ; add the LineEdit text to the list.

done            ANOP
                rts
                end

                EJECT
*******************************************************************************
*
doEdit          start
*
* Description:  This block is really 4 routines to handle the Edit Menu.
*               Any Edit selections are passed on to the appropriate
*               LineEdit routine. Undo is currently not supported.
*
*
* Inputs:       NONE
*
* Outputs:      NONE
*
* External Refs:
*               Import ckNewAndRep
*
* Entry Points:
*               entry doCut
*               entry doCopy
*               entry doPaste
*               entry doClear
*
*******************************************************************************
                using Globals

doCut           ENTRY
                PushLong LEHandle               ; Handle a "Cut" command
                _LECut
                bra done

doCopy          ENTRY
                PushLong LEHandle               ; Handle a "Copy" command
                _LECopy
                rts             ; Don't update New and Replace buttons

doPaste         ENTRY
                PushLong LEHandle               ; Handle a "Paste" command
                _LEPaste
                bra done

doClear         ENTRY
                PushLong LEHandle               ; Handle a "Clear" command
                _LEDelete

done            ANOP
                jsr ckNewAndRep ; update the New and Replace buttons

                rts
                end

                EJECT
*******************************************************************************
*
InContent       start
*
* Description:  Called to handle clicks in a window's contents. FindControl
*               is called to determine on what, if any, control was hit.
*               If a control is hit, it is tracked, and the appropriate
*               routine is called to perform some action. If a control
*               is not hit, then a check is made to see if the LineEdit
*               item was hit. If so, then the click is passed on to LEClick
*               for handling.
*
*
* Inputs:       NONE
*
* Outputs:      NONE
*
* External Refs:
*               Import NewListItem
*               Import DelListItem
*               Import RepListItem
*               Import RstListItem
*               Import ListHit
*               Import SortHit
*
* Entry Points: NONE
*
*******************************************************************************
                using Globals

; First, find out where we clicked, and see if it was on a control

                pha             ; result space
                PushLong #TempHndl
                PushLong EventWhere             ; push the point
                PushLong WindPointer
                _FindControl
                pla
                bne IC0010      ; hit a control! Track it!
                brl ckLine      ; didn't hit control. See if we hit LE
IC0010          ANOP
                pha             ; result space
                PushLong EventWhere             ; push mouse location
                PushLong #-1    ; use default action procedure
                PushLong tempHndl               ; push on handle to control hit
                _TrackControl   ; track it
                pla             ; where did we end up?
                bne IC0020      ; still in control. Do something
                brl done        ; out of control - exit this routine
IC0020          ANOP
                cmp #simpleButton               ; was it a button?
                beq CallCtlAction               ; if so then handle it
                cmp #$88        ; was it the list itself?
                bne done        ; if not, then not a hit!

; We use a neat trick to determine what routine to call inresponse to a
; click on a control. We keep a 'routine index' in the refcon field of
; the control. When a click occurs on that control, we retrieve the
; refcon, turn it into an index into a table of routine pointers, get
; the pointer to a routine, and call it!

CallCtlAction   ANOP
                pha             ; space for result
                pha
                PushLong tempHndl               ; get the item number from the refcon
                _GetCtlRefCon
                pla             ; low byte of ref con
                plx             ; (we don't use high byte)
                dec a           ; subtract 1 from the refcon then
                asl a           ; multiply by two to get table offset
                tax
                jsr (InCTable,x)                ; jump to routine to handle this ctl
                bra done

; the mouse click did not occur in any of the regular controls. check to
; see if we clicked in the LineEdit item. If so, then call LEClick.

ckLine          ANOP
                lda EventWhere  ; Check to see if we clicked in the
                sta LocalPt     ; LineEdit item. We do this by doing a
                lda EventWhere+2                ; PtInRect. But we need the mouse in
                sta LocalPt+2   ; local coordinates for this.

                PushLong #LocalPt
                _GlobalToLocal

                pha             ; space for result
                PushLong #LocalPt
                PushLong #destRect
                _PtInRect       ; see if we clicked in the edit item
                pla
                beq done        ; no, we didn't, so leave

                PushLong #EventRecord ; yes we did, so call LEClick
                PushLong LEHandle
                _LEClick

done            rts

theRefCon       ds 4
InCTable        dc i2'NewListItem,DelListItem,RepListItem'
                dc i2'RstListItem,ListHit,SortHit'
LocalPt         ds 4

                end

                EJECT
*******************************************************************************
*
NewListItem     start
*
* Description:  This routine is called by InContent if a click occured
*               on the "New" button. This routine determines is there
*               is enough room in the List record for another item. If
*               so, then a pointer to the next record is is created, and
*               AddMember is called to create a new List member using
*               the text from the LineEdit item. When that is done, we
*               tell the List Manager about it, and hilite the LineEdit
*               text so that it is easily changed into something else.
*
*
* Inputs:       NONE
*
* Outputs:      List updated. LineEdit text hilited.
*
* External Refs:
*               Import AddMember
*
* Entry Points: NONE
*
*******************************************************************************
                using Globals

                lda MLNum       ; # of elements in the list
                cmp #30         ; do we have 30 already?
                bge NLIDone
                inc MLNum       ; bump number of list entries

; Create a pointer to the next available space in the array of list record.
; This is done by multiplying the entry number the entry size. Multiplying
; is done by using the formula x = index*2 + index*8 (assuming that the size
; of a member record = 10)

                asl a           ; multiply by 2
                sta TempWord    ; save it for adding to *8 later
                asl a           ; by four
                asl a           ; and eight
                clc
                adc TempWord    ; add in the times 2 part

; AddMember is the routine that we use to assign the text of the LineEdit
; item to a certain list record. All we have to do is enter with the
; accumulator holding a pointer to the right record, and it does the rest.

                jsr AddMember   ; enter with record offset in Acc.

; Now tell the List Manager that we changed the list. Also, make sure that
; the item that is hilited in the list is STILL hilited. Do this by getting
; a pointer to the selected record, and passing it to _NewList

                pha             ; long space for result
                pha
                PushLong #0     ; start searching from the beginning
                PushLong #MyList
                _NextMember     ; Get pointer Keep it on the stack
                PushLong #MyList                ; pointer to my list record
                _NewList        ; and make the new list!

; finally, hilite the line edit text so that we can easily change it later

                PushWord #0     ; starting hilite range
                PushWord #255   ; ending range
                PushLong LEHandle               ; hande to the LE control
                _LESetSelect    ; hilite it.

NLIDone         ANOP
                rts

                end

                EJECT
*******************************************************************************
*
DelListItem     start
*
* Description:  Called by InContent to remove a member from the list. It
*               is called only when there is a hilited list item to be
*               removed. This routine determines which item is hilited,
*               removes it from the list of member records, decrements
*               the number of members in the list, and redraws the list.
*
*
* Inputs:       NONE
*
* Outputs:      NONE
*
* External Refs:
*               Import DisposeMember
*               Import NoSelection
*               Import ckNewAndRep
*
* Entry Points: NONE
*
*******************************************************************************
                using Globals

                lda MLNum       ; contains # of elements
                cmp #1          ; do we have only 1 left?
                beq DLIDone     ; if so then remove no more!
                dec MLNum       ; adjust number of list entries

; DisposeMember finds the hilited list item, and puts an index to it in the
; X register. Also, if the string for the name of the member was stored in
; a handle, that handle is disposed of.

                jsr DisposeMember               ; X set up for us on exit

loop            ANOP
                lda MyData+MemberSize,x
                sta MyData,x    ; move all data down 10 bytes
                inx             ; ...in memory...
                inx             ; ...two bytes at a time
                cpx #MyDataTSize                ; is X bumped past the end of records?
                blt loop        ; nope, keep looping

                PushLong #0     ; Draw All members to redraw the list!
                PushLong #MyList
                _NewList        ; and create the new list!

                jsr NoSelection ; now reset the apropriate buttons

DLIDone         ANOP
                jsr ckNewAndRep
                rts

                end

                EJECT
*******************************************************************************
*
RepListItem     start
*
* Description:  This routine is called from InContent to replace the text
*               of the hilited member with the text in the LineEdit Item.
*               It calls NextMember to find the hilited item, Disposes of
*               it, and then inserts a new item in the same place with the
*               new name.
*
*
* Inputs:       NONE
*
* Outputs:      NONE
*
* External Refs:
*               Import DisposeMember
*               Import AddMember
*               Import NoSelection
*
* Entry Points: NONE
*
*******************************************************************************
                using Globals

                pha             ; space for result
                pha
                PushLong #0     ; member to start looking at
                PushLong #MyList                ; NOTE: This is the list template ptr
                _NextMember
                PullLong tempLong               ; Pointer to item Selected

;
; Now that we know which member is being replaced, call DisposeMember to free
; up the memory that is allocated to it, and then call AddMember to fill in that
; member's record with new information.
;
                jsr DisposeMember
                txa
                jsr AddMember
                
; The information for the currently selected member has been changed. Now
; redraw the member.

                PushLong tempLong               ; pass ptr of member to redraw
                PushLong #MyList
                _DrawMember

                jsr NoSelection ; now reset the apropriate buttons

; finally, hilite the line edit text so that we can easily change it later

                PushWord #0     ; starting hilite range
                PushWord #255   ; ending range
                PushLong LEHandle               ; hande to the LE control
                _LESetSelect    ; hilite it.

                rts
                end

                EJECT
*******************************************************************************
*
RstListItem     start
*
* Description:  This routine is called by InContent to unhilite, or reset,
*               the currently selected item. It simply calls ResetMember
*               and then redraws that member.
*
*
* Inputs:       NONE
*
* Outputs:      NONE
*
* External Refs:                
*               Import NoSelection
*
* Entry Points: NONE
*
*******************************************************************************
                using Globals

                pha             ; space for result
                pha
                PushLong #MyList                ; NOTE: This Is the list template ptr
                _ResetMember    ; get item selected & unselect it
                PullLong tempLong               ; Item Selected

; Obviously, at this point we could have omitted the PullLong/PushLong
; sequence to save some bytes, but we wanted to make sure that people were
; clear on what ResetMember returned, and what was passed to DrawMember.

                PushLong tempLong               ; pass ptr of member to redraw
                PushLong #MyList                ; now redraw the list
                _DrawMember

                jsr NoSelection ; turn off those buttons
                rts
                end

                EJECT
*******************************************************************************
*
ListHit         start
*
* Description:  This routine is called by InContent when a hit on the List
*               item has occured. It activates the buttons we need when a
*               list item is selected, and gets the text of the item and
*               puts it into the LineEdit box. Finally, it hilites the
*               LineEdit text so that we can easily change it if we want.
*
*
* Inputs:       NONE
*
* Outputs:      NONE
*
* External Refs:                NONE
*
* Entry Points: NONE
*
*******************************************************************************
                using Globals

                PushWord #0     ; activate the delete button
                PushLong DelBHdl                ; handle to the delete button
                _HiLiteControl

                PushWord #255   ; now deactivate the replace button
                PushLong RepBHdl                ; until we change edit item
                _HiLiteControl

                PushWord #0     ; now activate the replace button
                PushLong RstBHdl
                _HiLiteControl

;
; Find out which member we hit, and insert its text into Line Edit
;
                pha             ; space for result
                pha
                PushLong #0     ; NIL to start at first entry
                PushLong #MyList                ; pointer to my list record...
                _NextMember
                PullLong deref  ; pointer to the next record

                ldy #2          ; get the pointer to the string. We do
                lda [deref],y   ; this by getting the pointer that the
                tax             ; handle points to, and storing it back
                lda [deref]     ; out in 'deref'.
                sta deref
                stx deref+2

                PushWord deref+2                ; push on a pointer to the first character
                lda deref
                ina
                pha
                lda [deref]     ; get the length of the string
                and #$00FF
                pha             ; push on the length
                PushLong LEHandle
                _LESetText
                
; Now invalidate the area of the screen that contained the text so that
; LineEdit will redraw it.

                PushLong #destRect
                _InvalRect

; finally, hilite the line edit text so that we can easily change it later

                PushWord #0     ; starting hilite range
                PushWord #255   ; ending range
                PushLong LEHandle               ; hande to the LE control
                _LESetSelect    ; hilite it.

                rts
                end

                EJECT
*******************************************************************************
*
SortHit         start
*
* Description:  This routine is called by InCotent when a click on the
*               Sort button occurs. It calls _SortList with a pointer to
*               a custom sorting routine, and then redraws the entire list.
*               The custom sorting procedure sorts the list in reverse
*               alphabetical order.
*
*
* Inputs:       NONE
*
* Outputs:      NONE
*
* External Refs:                NONE
*
* Entry Points: NONE
*
*******************************************************************************
                using Globals

                PushLong #MySort                ; Backwards sort routine
                PushLong #MyList
                _SortList

                PushLong #0     ; redraw all members
                PushLong #MyList
                _DrawMember

                rts

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
MySort          ANOP
;
; This is the custom sorting routine. It is passed the pointers to
; 2 member records to compare. If the first member is 'less than' the
; second, then return with the carry set. Otherwise, the carry should
; be returned clear. When I am done, remove the parameters off of the
; stack and RTL back to the List Manager.
;
; The following are equates that I use for a 'local direct page'. I
; save off the register that points to the normal direct page, and
; put the value of the stack pointer into the Direct Page register.
; This way, the stack doubles as my direct page, and I can easily
; access the variables that were pushed on it. These equates repre-
; sent the information on the stack when my routine is called.
; Specifically, there is a 3 byte return address that will get me
; back to the list manager, and then there are 2 4-byte parameters
; that are pointers to the 2 members I have to compare.
;
RTLAddr         equ 1
memberA         equ RTLAddr+3
memberB         equ memberA+4

                tsc             ; save the old Direct Page and
                phd             ; make the stack our DP so that
                tcd             ; we can use the params as pointers

                ldy #2          ; replace the pointer to the first
                lda [MemberA],y ; member with the pointer to the
                tax             ; string of that member.
                lda [MemberA]
                sta MemberA
                stx MemberA+2

                lda [MemberB],y ; do the same thing with the second
                tax             ; member.
                lda [MemberB]
                sta MemberB
                stx MemberB+2

                SHORT           ; Use 8-bit registers

                ldy #1          ; Start comparing with char #1
cmpLoop         ANOP
                lda [MemberB],y ; Now compare the string characters.
                cmp [MemberA],y ; If they are not equal, exit with the
                bne done        ; carry is set appropriately.

                tya             ; Check to see if we ended a string
                dec a
                cmp [memberA]   ; end of Member A?
                beq AGreater    ; if so, exit with carry set.

                cmp [memberB]   ; end of Member B?
                beq BGreater    ; if so, exit with carry clear.

                iny             ; bump index to examine next char.
                bra cmpLoop

AGreater        sec
                bra done
BGreater        clc
done            LONG

                pld             ; restore the Direct Page reg from stack.

                lda 0,s         ; move the RTL address up so that it is
                sta 8,s         ; in the right place after we remove the
                lda 2,s         ; parameters.
                sta 10,s
                pla             ; remove the pointers to the members from
                pla             ; the stack.
                pla
                pla
                rtl

                end

                EJECT
*******************************************************************************
*
ckNewAndRep     start
*
* Description:  Called from all over the place to correctly set the
*               activation states of the New and Replace buttons.
*
*
* Inputs:       NONE
*
* Outputs:      NONE
*
* External Refs:                NONE
*
* Entry Points: NONE
*
*******************************************************************************
                using Globals

; Disable the replace button if no items are selected

                pha             ; long space for result
                pha
                PushLong #0     ; start searching from the beginning
                PushLong #MyList
                _NextMember
                pla             ; was a pointer returned (if a long
                sta tempWord    ; zero is return, there are not items
                pla             ; selected).
                ora TempWord
                beq DisableRepItem

                PushWord #0     ; activate the replace button
                bra FixRepButton

DisableRepItem  ANOP
                PushWord #255   ; deactivate the replace button

FixRepButton    ANOP
                PushLong RepBHdl
                _HiLiteControl

; Enable or disable the New entry button depending on whether or not there
; is any text in the LineEdit buffer.

                pha
                PushLong LEHandle
                _LEGetTextLen
                pla
                beq disableIt   ; if no length, then no text.

                PushWord #0     ; activate the New button
                bra CallHilite

disableIt       ANOP
                PushWord #255   ; deactivate the New button

CallHilite      ANOP
                PushLong NewBHdl
                _HiLiteControl

                RTS
                end

                EJECT
*******************************************************************************
*
AddMember       start
*
* Description:  Utility routine called from NewListItem and RepListItem to
*               insert a new member. This routine will read the entry in
*               the LineEdit record, store it in a handle, and create a
*               new member record at the spot that is specified by the
*               accumulator. It does not redraw the list.
*
*
* Inputs:       A = the offset to the record that you want to change.
*
* Outputs:      X = the offset to the record that was changed.
*
* External Refs:                NONE
*
* Entry Points: NONE
*
*******************************************************************************
                using Globals

                sta RecordIndex

                pha             ; space for handle
                pha
                PushLong LEHandle
                _LEGetTextHand  ; get the text from Line Edit
                PullLong TempHandle

                pha
                PushLong LEHandle
                _LEGetTextLen   ; find out how large it is.
                PullWord StringSize

                pha             ; make room for NewHandle result
                pha
                PushWord #0     ; push handle size that we want
                lda StringSize  ; but add 1 to it to take into
                inc a           ; account the string length byte
                pha
                PushWord MyID
                PushWord #attrFixed+attrNoCross
                PushLong #0
                _NewHandle      ; create a handle for the string

; save the handle we got in 'StringHandle' so that we can later store it in the
; member's record. Also store it in 'deref' so that we can dereference it. The
; macro PullLong addr1,addr2 will do that for us in one step!

                PullLong StringHandle,deref

                ldy #2          ; dereference the String Handle to get
                lda [deref],y   ; just a pointer.
                tax
                lda [deref]
                sta deref
                stx deref+2

                lda StringSize  ; create a Pascal string from the lineEdit
                sta [deref]     ; item for the List Manager

; Copy the LineEdit string into the string's handle. However, set the
; destination to be the second byte of the handle, as we need to put the
; length of the string in the first byte.

                PushLong TempHandle             ; handle of the source string
                lda deref+2     ; deref holds a pointer to the start
                pha             ; of the destination, but we have to bump
                lda deref       ; by one to account for the length byte
                inc a           ; that we have to stick there.
                pha
                PushWord #0     ; bytes to transfer must be LONG, so
                PushWord StringSize             ; create a high byte of zero.
                _HandToPtr

; initialize the data in the member record we are creating.

                ldx RecordIndex
                lda deref       ; get low word of address of new string
                sta MyData,x    ; and save it
                lda deref+2     ; and now the high word
                sta MyData+2,x
                lda #0          ; store a memflag byte
                sta MyData+4,x
                lda StringHandle                ; store the handle to the string so that
                sta MyData+6,x  ; we can easily dispose of it later.
                lda StringHandle+2
                sta MyData+8,x

                rts

StringHandle    ds 4
TempHandle      ds 4
StringSize      ds 2
RecordIndex     ds 2

                end

                EJECT
*******************************************************************************
*
DisposeMember   start
*
* Description:  Find the currently hilited list member and and return
*               with an offset to it. Will also dispose of any memory
*               associated with it to hold the displayed text.
*
*
* Inputs:       NONE
*
* Outputs:      X = offset from start of Member record array where the
*                   currently hilited member is stored.
*
* External Refs:                NONE
*
* Entry Points: NONE
*
*******************************************************************************
                using Globals

; Call _NextMember to find the hilited item. Calculate the offset
; within MyData of that record, and dispose of any string handles
; that may be there.

                pha             ; space for result
                pha
                PushLong #0     ; NIL to start at first entry
                PushLong #MyList                ; pointer to my list record.
                _NextMember
                pla             ; pointer to the next record
                plx             ; discard the high byte
                sec             ; subtract the front of the list to
                sbc #MyData     ; get the offset into the table
                tax             ; put into X so we can adjust the list
                stx recordIndex ; save it here for when we exit

                lda MyData+6+2,x                ; See if there's a string handle by
                ora MyData+6,x  ; checking for a NULL handle.
                beq done        ; no string - so nothing to dispose of

                PushLong MyData+6,x             ; get the string handle and dispose of it
                _DisposeHandle
done            ANOP
                ldx RecordIndex ; return with offset in X register.
                rts

RecordIndex     ds 2

                end


                EJECT
*******************************************************************************
*
NoSelection     start
*
* Description:  Set the activation state of the buttons to reflect the
*               fact that no list items are selected.
*
*
* Inputs:       NONE
*
* Outputs:      NONE
*
* External Refs:                NONE
*
* Entry Points: NONE
*
*******************************************************************************
                using Globals

                PushWord #255   ; deactivate the delete button
                PushLong DelBHdl                ; handle to the delete button
                _HiLiteControl

                PushWord #255   ; deactivate the reset button
                PushLong RstBHdl
                _HiLiteControl

                PushWord #255   ; deactivate the replace button
                PushLong RepBHdl
                _HiLiteControl

                rts
                end

                EJECT
*******************************************************************************
*
CreateCtrls     start
*
* Description:  Called at initialization time to create all of the
*               simple buttons we use.
*
*
* Inputs:       NONE
*
* Outputs:      NONE
*
* External Refs:                NONE
*
* Entry Points: NONE
*
*******************************************************************************
                using Globals

                stz CCCount     ; zero our counter
CCLoop          ANOP
                lda CCCount     ; get the next ctrl num to add
                asl a           ; multiply it by four to make it an
                asl a           ; offset into our control table
                tax             ; now use it as an index
                lda MyCtrlList,x                ; to get the pointer to the control data
                sta deref       ; and save it in the DPage
                lda MyCtrlList+2,x              ; so we can read it nicely
                sta deref+2
                stx tempWord    ; save x for a moment

                pha             ; space for result
                pha
                PushLong WindPointer ; pointer to my window

; Push on NewControl parameters. These parameters are obtained from tables
; in our Globals section. Each one is 'cRecSize' long, so we load the Y reg
; with that value (less 2), and use it to access all of the parameters in 
; the table, pushing each one on the stack as we go.

                ldy #cRecSize-2
CCL010          lda [deref],y   ; Get these parameters from a list of
                pha             ; data blocks in the Globals data
                dey             ; section.
                dey
                bpl CCL010      ; if not done, get the next one!

                _NewControl     ; if so, then just call new control!

                ldx tempWord    ; retrieve x
                pla             ; and save the control handle
                sta MyCtlHdls,x
                pla
                sta MyCtlHdls+2,x

                inc CCCount     ; bump counter to get next record
                lda CCCount
                cmp NumCtrls    ; test to see if we did the last one
                blt CCLoop      ; if so, then just end

;
; Create the LineEdit item.
;
                pha             ; space for result
                pha
                PushLong #destRect
                PushLong #destRect
                PushWord #20    ; Allow only 20 chars in list item
                _LENew
                PullLong LEHandle

                rts

tempHandle      ds 4
CCCount         ds 2

                end

                EJECT
*******************************************************************************
*
CreateList      start
*
* Description:  Called at initialization time to create the list item. It
*               simply calls _CreateList with a parameter block defined
*               in the Globals data section.
*
*
* Inputs:       NONE
*
* Outputs:      NONE
*
* External Refs:                NONE
*
* Entry Points: NONE
*
*******************************************************************************
                using Globals

                pha             ; room for result
                pha
                PushLong WindPointer
                PushLong #MyList                ; pointer to list rec
                _CreateList
                PullLong ListCtlHndl ; and get our list control handle

                rts
                end

                EJECT
*******************************************************************************
*
doQuit          start
*
* Description:  Quit routine. Set the QuitFlag to TRUE for the EventLoop.
*
*
* Inputs:       NONE
*
* Outputs:      QuitFlag set to $FFFF
*
* External Refs:                NONE
*
* Entry Points: NONE
*
*******************************************************************************
                using Globals

                lda #$FFFF
                sta QuitFlag
                rts
                end

                copy lists.inits.aii

                END
